package com.google.gwt.gwtpages.client;


import java.util.ArrayList;

import test.com.google.gwt.gwtpages.client.MockGWTPagesSettings;

import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.gwtpages.client.message.DefaultPageMessageHandler;
import com.google.gwt.gwtpages.client.page.ApplicationPresenter;
import com.google.gwt.gwtpages.client.page.DefaultPageLoadingHandler;
import com.google.gwt.gwtpages.client.page.GWTPages;
import com.google.gwt.gwtpages.client.page.PageEventHandler;
import com.google.gwt.gwtpages.client.page.loader.GWTPagesLoader;
import com.google.gwt.gwtpages.client.page.parameters.GWTPagesTokenizer;
import com.google.gwt.gwtpages.client.page.parameters.SimpleGWTPagesTokenizer;
import com.google.gwt.user.client.History;

/**
 * Main entry point to configure GWT Pages functionality. The init method *must*
 * be called before using GWT Pages.
 * 
 * @author Joe Hudson
 */
public class GWTPagesSettings {

	private static GWTPagesSettings instance;

	protected ApplicationPresenter pagePresenter;
	protected GWTPagesTokenizer pageTokenizer;
	protected GWTPagesLoader pageLoader;
	protected ArrayList<PageEventHandler> pageEventHandlers = new ArrayList<PageEventHandler>();
	protected HandlerManager bus;

	/**
	 * Allow subclasses to lazy load the settings
	 */
	public GWTPagesSettings () {
		if (null == instance) instance = this;
	}
	
	public static GWTPagesSettings get() {
		if (null == instance) instance = new MockGWTPagesSettings();
		return instance;
	}

	public static GWTPages init(ApplicationPresenter pagePresenter,
			GWTPagesLoader pageLoader, HandlerManager bus, boolean monitorHistoryEvents) {
		return init(pagePresenter, pageLoader, bus, GWTPages.get(), monitorHistoryEvents);
	}

	public static GWTPages init(ApplicationPresenter pagePresenter,
			GWTPagesLoader pageLoader, HandlerManager bus, GWTPages pagesInstance, boolean monitorHistoryEvents) {
		GWTPagesSettings instance = get();
		instance.pagePresenter = pagePresenter;
		instance.pageLoader = pageLoader;
		instance.bus = bus;
		instance.init();
		if (monitorHistoryEvents)
			History.addValueChangeHandler(pagesInstance);
		return pagesInstance;
	}
	
	/**
	 * @param pagePresenter
	 *            the presenter used to handle the page component for display
	 * @param pageLoader
	 *            the loader used to retrieve and load the application pages
	 * @param bus
	 *            the common event manager
	 */
	public GWTPagesSettings (ApplicationPresenter pagePresenter,
			GWTPagesLoader pageLoader, HandlerManager bus) {
		if (null == instance) instance = this;
		this.pagePresenter = pagePresenter;
		this.pageLoader = pageLoader;
		this.bus = bus;
		init();
	}

	/**
	 * @param pagePresenter
	 *            the presenter used to handle the page component for display
	 * @param pageLoader
	 *            the loader used to retrieve and load the application pages
	 */
	public GWTPagesSettings (ApplicationPresenter pagePresenter,
			GWTPagesLoader pageLoader) {
		this.pagePresenter = pagePresenter;
		this.pageLoader = pageLoader;
		this.bus = new HandlerManager(null);
		init();
	}

	protected void init() {
		pageLoader.init(this);
	}

	/**
	 * Add default event handlers for handling page request messages as well as
	 * notifying the user when pages are being loaded.  The handlers that are added are
	 * <ul>
	 * 	<ol>
	 * 		<li>{@link DefaultPageLoadingHandler}</li>
	 * 		<li>{@link DefaultPageMessageHandler}</li>
	 * </ul>
	 */
	public GWTPagesSettings addDefaultEventHandlers() {
		add(new DefaultPageLoadingHandler(), new DefaultPageMessageHandler());
		return this;
	}
	
	/**
	 * Return the {@link ApplicationPresenter}
	 */
	public ApplicationPresenter getApplicationPresenter() {
		return pagePresenter;
	}

	/**
	 * Return the {@link GWTPagesTokenizer}. If not set, {@link SimpleGWTPagesTokenizer}
	 * will be used.
	 */
	public GWTPagesTokenizer getPageTokenizer() {
		if (null == pageTokenizer) {
			pageTokenizer = new SimpleGWTPagesTokenizer();
			pageTokenizer.init(this);
		}
		return pageTokenizer;
	}

	/**
	 * Set the {@link GWTPagesTokenizer}. If not set, {@link SimpleGWTPagesTokenizer}
	 * will be used.
	 * 
	 * @param pageTokenizer
	 *            the page tokenizer
	 * @return the Settings
	 */
	public GWTPagesSettings setPageTokenizer(GWTPagesTokenizer pageTokenizer) {
		this.pageTokenizer = pageTokenizer;
		pageTokenizer.init(this);
		return this;
	}

	/**
	 * Return the {@link GWTPagesLoader}.
	 */
	public GWTPagesLoader getPageLoader() {
		return pageLoader;
	}

	/**
	 * Return the common event bus
	 */
	public HandlerManager getBus() {
		return bus;
	}

	/**
	 * Add page lifecycle event handler
	 * 
	 * @param pageEventHandlers
	 *            the handler(s)
	 * @return the Settings
	 */
	public GWTPagesSettings add(PageEventHandler... pageEventHandlers) {
		for (PageEventHandler pageEventHandler : pageEventHandlers) {
			this.pageEventHandlers.add(pageEventHandler);
		}
		return this;
	}

	/**
	 * Return a list of all defined page lifecycle event handlers (or empty list
	 * if none defined)
	 */
	public ArrayList<PageEventHandler> getPageEventHandlers() {
		return pageEventHandlers;
	}
}